package ddz.core.ai;

import com.kaka.util.IntArray;
import com.kaka.util.IntSet;
import com.kaka.util.MathUtils;
import ddz.core.CardType;
import ddz.core.Cards;
import ddz.core.Pile;
import ddz.core.SuitType;

import java.util.*;

/**
 * 机器人AI
 */
public class AIBot {

    static Map<Integer, String> cardPointMap = new HashMap<>(14);

    static {
        String[] s = new String[]{"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"};
        for (int i = 0; i < s.length; i++) {
            cardPointMap.put(i + 3, s[i]);
        }
        cardPointMap.put(2, "2");
        cardPointMap.put(500, "小王");
        cardPointMap.put(600, "大王");
    }

    /**
     * 分析手牌中各种牌簇之间的关系
     *
     * @param mycards 手牌
     */
    public static RelateInfo analyseRelateInfo(Cards mycards, IntSet removedCards) {
        RelateInfo info = new RelateInfo(mycards.getPileCount());
        Pile danPile = null, duiPile = null, sanPile = null;
        IntSet youguan = new IntSet(mycards.getPileCount());
        IntSet sanLian = new IntSet(mycards.getPileCount());
        IntSet duiLian = new IntSet(mycards.getPileCount());
        IntSet danLian = new IntSet(mycards.getPileCount());
        for (int i = mycards.getMinValidIndex(); i <= mycards.getMaxValidIndex(); i++) {
            Pile pile = mycards.getPileByIndex(i);
            if (pile == null || pile.getCount() == 0) continue;
            if (pile.getCount() == 3) {
                info.types.add(WeightType.san);
            } else if (pile.getCount() == 2) {
                info.types.add(WeightType.dui);
            } else if (pile.getCount() == 4) {
                info.types.add(WeightType.zha);
            }
            if (pile.getCount() >= 1 && danPile == null) danPile = pile;
            if (pile.getCount() >= 2 && duiPile == null) duiPile = pile;
            if (pile.getCount() >= 3 && sanPile == null) sanPile = pile;
            if (sanPile != pile && sanPile != null) {
                if (sanPile.getCount() == 3 && pile.getCount() == 3 && sanPile.isSequenceOf(pile)) {
                    //因拆炸弹组成三顺不值得，所以判断牌张数等于3的三顺就足够
                    sanLian.add(sanPile.getPoint());
                    sanLian.add(pile.getPoint());
                } else {
                    if (sanLian.size() >= 2) {
                        info.types.add(WeightType.sanShun);
                        youguan.addAll(sanLian);
                    }
                    sanLian.clear();
                }
                sanPile = pile;
            }
            if (duiPile != pile && duiPile != null) {
                //炸弹不参与连牌
                if (duiPile.getCount() >= 2 && pile.getCount() >= 2 && pile.getCount() < 4 && duiPile.isSequenceOf(pile)) {
                    duiLian.add(duiPile.getPoint());
                    duiLian.add(pile.getPoint());
                    //if (duiPile.getCount() == 4 || pile.getCount() == 4) {
                    //    //因为炸可以被拆成两对，可组成两个连对
                    //    info.types.add(WeightType.zha);
                    //}
                } else {
                    if (duiLian.size() >= 3) {
                        info.types.add(WeightType.duiShun);
                        youguan.addAll(duiLian);
                    }
                    duiLian.clear();
                }
                duiPile = pile;
            }
            if (danPile != pile && danPile != null) {
                if (danPile.getCount() >= 1 && pile.getCount() >= 1 && danPile.isSequenceOf(pile)) {
                    danLian.add(danPile.getPoint());
                    danLian.add(pile.getPoint());
                } else {
                    if (danLian.size() >= 5) {
                        info.types.add(WeightType.danShun);
                        youguan.addAll(danLian);
                    }
                    danLian.clear();
                }
                danPile = pile;
            }
        }
        if (sanLian.size() >= 2) {
            info.types.add(WeightType.sanShun);
            youguan.addAll(sanLian);
        }
        if (duiLian.size() >= 3) {
            info.types.add(WeightType.duiShun);
            youguan.addAll(duiLian);
        }
        if (danLian.size() >= 5) {
            info.types.add(WeightType.danShun);
            youguan.addAll(danLian);
        }
        for (int i = mycards.getMinValidIndex(); i <= mycards.getMaxValidIndex(); i++) {
            Pile pile = mycards.getPileByIndex(i);
            if (pile != null && pile.getCount() > 0) {
                if (!youguan.contains(pile.getPoint())) {
                    Pile removedPile = mycards.removePile(i);
                    if (removedPile != null && removedPile.getCount() > 0) {
                        if (removedCards != null) {
                            int[] cards = removedPile.getCards();
                            for (int card : cards) {
                                removedCards.add(card);
                            }
                        }
                        info.unrelates.add(pile);
                    }
                }
            }
        }
        if (info.types.contains(WeightType.sanShun) && info.types.contains(WeightType.san)) {
            //有三顺就计算三张了
            info.types.remove(WeightType.san);
        }
        if (info.types.contains(WeightType.duiShun) && info.types.contains(WeightType.dui)) {
            //有对顺就计算对了
            info.types.remove(WeightType.dui);
        }
        return info;
    }

    /**
     * 移除指定数量的pile
     *
     * @param mycards      手牌
     * @param num          指定数量
     * @param removedCards 被移除的牌暂存器，用于后期手牌还原
     * @return 被移除的牌的集合
     */
    static List<int[]> remove(Cards mycards, int num, IntArray removedCards) {
        List<int[]> groups = new ArrayList<>(mycards.getPileCount());
        for (int i = mycards.getMinValidIndex(); i <= mycards.getMaxValidIndex(); i++) {
            Pile pile = mycards.getPileByIndex(i);
            if (pile == null || pile.getCount() == 0) continue;
            if (pile.getCount() == num) {
                int[] cards = pile.getCards();
                groups.add(cards);
                mycards.removePile(i);
                for (int card : cards) {
                    removedCards.add(card);
                }
            }
        }
        return groups;
    }

    /**
     * 计算权重
     *
     * @param pile
     * @param map
     * @return
     */
    static int calcWeight(Pile pile, Map<WeightType, List<int[]>> map) {
        List<int[]> groups = null;
        int weight = 0;
        if (pile.getCount() == 1) {
            weight += WeightType.dan.weight;
            groups = map.computeIfAbsent(WeightType.dan, k -> new ArrayList<>());
        } else if (pile.getCount() == 2) {
            if (pile.getPoint() == 0) {
                weight += WeightType.zha.weight;
                groups = map.computeIfAbsent(WeightType.zha, k -> new ArrayList<>());
            } else {
                weight += WeightType.dui.weight;
                groups = map.computeIfAbsent(WeightType.dui, k -> new ArrayList<>());
            }
        } else if (pile.getCount() == 3) {
            weight += WeightType.san.weight;
            groups = map.computeIfAbsent(WeightType.san, k -> new ArrayList<>());
        } else if (pile.getCount() == 4) {
            weight += WeightType.zha.weight;
            groups = map.computeIfAbsent(WeightType.zha, k -> new ArrayList<>());
        }
        if (groups != null) {
            groups.add(pile.getCards());
        }
        return weight;
    }

    /**
     * 分析手牌手数
     *
     * @param mycards 手牌
     */
    public static HandsInfo analyseHands(Cards mycards) {
        IntSet analyseRelateRemoved = new IntSet();
        RelateInfo info = analyseRelateInfo(mycards, analyseRelateRemoved);
        info.types.remove(WeightType.zha);
        WeightType[] types = new WeightType[info.types.size()];
        info.types.toArray(types);
        int[] permSrc = new int[types.length];
        for (int i = 0; i < permSrc.length; i++) {
            permSrc[i] = i;
        }
        List<HandsInfo> handsInfoList = new ArrayList<>();
        MathUtils.permutate(permSrc, (int[] newPerm) -> {
//            WeightType[] newTypes = new WeightType[types.length];
//            for (int i = 0; i < newTypes.length; i++) {
//                newTypes[i] = types[newPerm[i]];
//            }
//            System.out.println(Arrays.toString(newTypes));
            int weight = 0;
            Map<WeightType, List<int[]>> map = new HashMap<>(7);
            IntArray removedCards = new IntArray(mycards.getCardCount());
            for (int k = 0; k < newPerm.length; k++) {
                WeightType ctype = types[newPerm[k]];
                if (ctype == WeightType.sanShun) {
                    List<IntArray> sanShun = shun(mycards, -1, 3, 2, removedCards);
                    if (sanShun != null && !sanShun.isEmpty()) {
                        List<int[]> sanShunArrayList = new ArrayList<>(sanShun.size());
                        for (IntArray array : sanShun) {
                            sanShunArrayList.add(array.toArray());
                            weight += WeightType.sanShun.weight + (array.size() - 2) * 3;
                        }
                        map.put(WeightType.sanShun, sanShunArrayList);
                    }
                } else if (ctype == WeightType.danShun) {
                    List<IntArray> danShun = shun(mycards, -1, 1, 5, removedCards);
                    if (danShun != null && !danShun.isEmpty()) {
                        List<int[]> danShunArrayList = new ArrayList<>(danShun.size());
                        for (IntArray array : danShun) {
                            danShunArrayList.add(array.toArray());
                            weight += WeightType.danShun.weight + (array.size() - 5);
                        }
                        map.put(WeightType.danShun, danShunArrayList);
                    }
                } else if (ctype == WeightType.duiShun) {
                    List<IntArray> duiShun = shun(mycards, -1, 2, 3, removedCards);
                    if (duiShun != null && !duiShun.isEmpty()) {
                        List<int[]> duiShunArrayList = new ArrayList<>(duiShun.size());
                        for (IntArray array : duiShun) {
                            duiShunArrayList.add(array.toArray());
                            weight += WeightType.duiShun.weight + (array.size() - 3) * 2;
                        }
                        map.put(WeightType.duiShun, duiShunArrayList);
                    }
                } else if (ctype == WeightType.dui) {
                    List<int[]> duiArrayList = remove(mycards, 2, removedCards);
                    if (!duiArrayList.isEmpty()) {
                        weight += WeightType.dui.weight * duiArrayList.size();
                        map.put(WeightType.dui, duiArrayList);
                    }
                } else if (ctype == WeightType.san) {
                    List<int[]> sanArrayList = remove(mycards, 3, removedCards);
                    if (!sanArrayList.isEmpty()) {
                        weight += WeightType.san.weight * sanArrayList.size();
                        map.put(WeightType.san, sanArrayList);
                    }
                } else if (ctype == WeightType.zha) {
                    List<int[]> zhaArrayList = remove(mycards, 4, removedCards);
                    if (!zhaArrayList.isEmpty()) {
                        weight += WeightType.zha.weight * zhaArrayList.size();
                        map.put(WeightType.zha, zhaArrayList);
                    }
                }
            }
            for (int i = mycards.getMinValidIndex(); i <= mycards.getMaxValidIndex(); i++) {
                Pile pile = mycards.getPileByIndex(i);
                if (pile == null || pile.getCount() == 0) continue;
                weight += calcWeight(pile, map);
            }
            if (info.unrelates != null && !info.unrelates.isEmpty()) {
                for (Pile pile : info.unrelates) {
                    weight += calcWeight(pile, map);
                }
            }
            Map<WeightType, Integer> handsMap = new HashMap<>(map.size());
            map.forEach((WeightType type, List<int[]> groups) -> {
                int hands = handsMap.getOrDefault(type, 0);
                hands += groups.size();
                handsMap.put(type, hands);
            });
            if (handsMap.containsKey(WeightType.sanShun)) {
                int danHands = handsMap.getOrDefault(WeightType.dan, 0);
                int duiHands = handsMap.getOrDefault(WeightType.dui, 0);
                List<int[]> sanShunGroups = map.get(WeightType.sanShun);
                for (int[] group : sanShunGroups) {
                    int pileCount = group.length / 3;
                    if (danHands >= pileCount) {
                        danHands -= pileCount;
                    } else if (duiHands >= pileCount) {
                        duiHands -= pileCount;
                    }
                }
                handsMap.put(WeightType.dan, danHands);
                handsMap.put(WeightType.dui, duiHands);
            }
            if (handsMap.containsKey(WeightType.san)) {
                int danHands = handsMap.getOrDefault(WeightType.dan, 0);
                int duiHands = handsMap.getOrDefault(WeightType.dui, 0);
                List<int[]> sanGroups = map.get(WeightType.san);
                for (int[] group : sanGroups) {
                    int pileCount = group.length / 3;
                    if (danHands >= pileCount) {
                        danHands -= pileCount;
                    } else if (duiHands >= pileCount) {
                        duiHands -= pileCount;
                    }
                }
                handsMap.put(WeightType.dan, danHands);
                handsMap.put(WeightType.dui, duiHands);
            }
            if (handsMap.containsKey(WeightType.zha)) {
                int danHands = handsMap.getOrDefault(WeightType.dan, 0);
                int duiHands = handsMap.getOrDefault(WeightType.dui, 0);
                List<int[]> zhaGroups = map.get(WeightType.zha);
                for (int[] group : zhaGroups) {
                    if (group.length == 4) {
                        if (danHands >= 2) {
                            danHands -= 2;
                        } else if (duiHands >= 2) {
                            duiHands -= 2;
                        }
                    }
                }
                handsMap.put(WeightType.dan, danHands);
                handsMap.put(WeightType.dui, duiHands);
            }
            int[] hands = new int[1];
            handsMap.forEach((WeightType type, Integer handsVal) -> {
                hands[0] += handsVal;
            });
            //还原手牌
            for (int j = 0; j < removedCards.size(); j++) {
                mycards.addCard(removedCards.get(j));
            }
            removedCards.clear();
//            System.out.println("权值：" + weight + "  手数：" + hands[0]);
//            List<int[]> allGroups = new ArrayList<>();
//            map.forEach((WeightType type, List<int[]> groups) -> {
//                allGroups.addAll(groups);
//            });
//            System.out.println(toString(allGroups));
            handsInfoList.add(new HandsInfo(weight, hands[0], map));
        });
        handsInfoList.sort((HandsInfo info1, HandsInfo info2) -> {
            if (info2.hands < info1.hands) return 1;
            if (info2.hands > info1.hands) return -1;
            if (info2.weight > info1.weight) return 1;
            if (info2.weight < info1.weight) return -1;
            return 0;
        });

        IntSet.IntSetIterator iterator = analyseRelateRemoved.iterator();
        while (iterator.hasNext) {
            int card = iterator.next();
            mycards.addCard(card);
        }
        HandsInfo handsInfo = handsInfoList.get(0);
        List<int[]> dan = handsInfo.map.get(WeightType.dan);
        if (dan != null && !dan.isEmpty()) {
            if (dan.size() >= 2) { //单牌排序
                dan.sort((int[] dan1, int[] dan2) -> Cards.compare(dan1[0], dan2[0]));
            }
        }
//        List<int[]> allGroups = new ArrayList<>();
//        handsInfo.map.forEach((WeightType type, List<int[]> groups) -> {
//            allGroups.addAll(groups);
//        });
//        System.out.println(toString(allGroups));
        return handsInfo;
    }

    public static StringBuilder toString(int[] cards) {
        StringBuilder sb1 = new StringBuilder();
        sb1.append('[');
        for (int j = 0; j < cards.length; j++) {
            int card = cards[j];
            int suit = Cards.getSuit(card);
            int point = Cards.getPoint(card);
            if (point == 0) {
                sb1.append(cardPointMap.get(card));
            } else {
                sb1.append(SuitType.getSuitName(suit)).append(cardPointMap.get(point));
            }
            if (j != cards.length - 1) {
                sb1.append(", ");
            }
        }
        sb1.append(']');
        return sb1;
    }

    public static String toString(List<int[]> groups) {
        StringBuilder sb = new StringBuilder();
        sb.append('[');
        int size = groups.size();
        for (int i = 0; i < size; i++) {
            int[] group = groups.get(i);
            StringBuilder sb1 = toString(group);
            if (i != size - 1) {
                sb1.append(", ");
            }
            sb.append(sb1);
        }
        sb.append(']');
        return sb.toString();
    }

    /**
     * 找出所有的连牌
     *
     * @param mycards      手牌
     * @param minCard      最小牌参照
     * @param type         1为单顺，2为双顺，3为三顺，4为四顺
     * @param shunCount    最小顺数量，一般单顺最小顺为5顺，双顺、三顺、四顺最小顺为2顺
     * @param groups       顺牌组合的集合
     * @param tempList     一组顺牌
     * @param removedCards 删除的牌，即抽取出来已组成顺子的牌
     */
    private static void allMinShun(Cards mycards, int minCard, int type, int shunCount, List<IntArray> groups, List<Pile> tempList, IntArray removedCards) {
        for (int i = mycards.getMinValidIndex(); i <= mycards.getMaxValidIndex(); i++) {
            Pile pile = mycards.getPileByIndex(i);
            if (pile == null || pile.getCount() < type || pile.compare(minCard) <= 0) {
                tempList.clear();
                continue;
            }
            if (tempList.isEmpty()) {
                tempList.add(pile);
                continue;
            }
            Pile tempLast = tempList.get(tempList.size() - 1);
            if (tempLast.isSequenceOf(pile)) {
                tempList.add(pile);
                int size = tempList.size();
                if (size >= shunCount) {
                    IntArray group = new IntArray(shunCount);
                    for (int j = 0; j < size; j++) {
                        int[] cards = tempList.get(j).getCards();
                        if (cards == null) continue;
                        for (int k = 0; k < type; k++) {
                            mycards.removeCard(cards[k]);
                            if (removedCards != null) removedCards.add(cards[k]);
                            group.add(cards[k]);
                        }
                    }
                    groups.add(group);
                    tempList.clear();
                    allMinShun(mycards, minCard, type, shunCount, groups, tempList, removedCards);
                    break;
                }
            } else {
                tempList.clear();
            }
        }
    }

    /**
     * 扩展和合并顺牌，得到所有最长的顺牌 </br>
     * 经此计算，手牌数据将发生变化
     *
     * @param mycards      手牌
     * @param minCard      最小牌参照
     * @param type         1为单顺，2为双顺，3为三顺，4为四顺
     * @param shunCount    最小顺数量，一般单顺最小顺为5顺，双顺、三顺、四顺最小顺为2顺
     * @param removedCards 删除的牌，即抽取出来已组成顺子的牌
     * @return 顺牌组合的集合
     */
    public static List<IntArray> shun(Cards mycards, int minCard, int type, int shunCount, IntArray removedCards) {
        List<IntArray> groups = new ArrayList<>();
        List<Pile> tempList = new ArrayList<>(mycards.getPileCount());
        Pile pile0 = mycards.removePile(Cards.getIndex(0));
        Pile pile2 = mycards.removePile(Cards.getIndex(2));
        allMinShun(mycards, minCard, type, shunCount, groups, tempList, removedCards);
        if (!groups.isEmpty()) {
            for (int i = mycards.getMinValidIndex(); i <= mycards.getMaxValidIndex(); i++) {
                Pile pile = mycards.getPileByIndex(i);
                if (pile == null || pile.getCount() < type) continue;
                for (IntArray group : groups) {
                    int minPoint = Cards.getPoint(group.get(0));
                    if (minPoint - pile.getPoint() == 1) {
                        int[] cards = pile.getCards();
                        if (cards != null && cards.length >= type) {
                            for (int k = 0; k < type; k++) {
                                group.add(cards[k]);
                                mycards.removeCard(cards[k]);
                                if (removedCards != null) removedCards.add(cards[k]);
                            }
                        }
                    } else {
                        int maxPoint = Cards.getPoint(group.get(group.size() - 1));
                        if (maxPoint - pile.getPoint() == -1) {
                            int[] cards = pile.getCards();
                            if (cards != null && cards.length >= type) {
                                for (int k = 0; k < type; k++) {
                                    group.add(cards[k]);
                                    mycards.removeCard(cards[k]);
                                    if (removedCards != null) removedCards.add(cards[k]);
                                }
                            }
                        }
                    }
                }
            }
        }
        mycards.insertPile(pile0);
        mycards.insertPile(pile2);
        if (groups.size() > 1) {
            //合并顺牌
            int i = 0;
            while (i < groups.size()) {
                IntArray first = groups.get(i);
                int j = i + 1;
                boolean isMerge = false;
                while (j < groups.size()) {
                    IntArray group = groups.get(j);
                    int minPoint1 = Cards.getPoint(first.get(0));
                    int maxPoint1 = Cards.getPoint(first.get(first.size() - 1));
                    int minPoint2 = Cards.getPoint(group.get(0));
                    int maxPoint2 = Cards.getPoint(group.get(group.size() - 1));
                    if (maxPoint2 - minPoint1 == -1 || maxPoint1 - minPoint2 == -1) {
                        first.addAll(group);
                        isMerge = true;
                        groups.remove(j);
                    } else {
                        j++;
                    }
                }
                if (!isMerge) i++;
            }
        }
        return groups;
    }

    /**
     * 单牌 </br>
     * 如果手中有单牌，则跟之，否则拆2跟之，否则拆对牌跟之，否则拆6连以上的单顺顶张跟之，否则拆三条跟之，
     * 否则拆三顺跟之，否则拆5连单顺跟之，否则拆双顺跟之，否则炸之，否则PASS
     *
     * @param mycards
     * @param minCard
     * @return
     */
    static List<int[]> dan(Cards mycards, int minCard, boolean maxDan) {
        if (mycards.getCardCount() == 1) {
            int[] cards = mycards.getCards();
            if (Cards.compare(cards[0], minCard) > 0) {
                List<int[]> newList = new ArrayList<>(1);
                newList.add(cards);
                return newList;
            }
            return null;
        }
        HandsInfo info = analyseHands(mycards);
        Map<WeightType, List<int[]>> map = info.map;
        List<int[]> list = map.get(WeightType.dan);
        //有单出单
        if (list != null && !list.isEmpty()) {
            if (maxDan) {
                for (int i = list.size() - 1; i >= 0; i--) {
                    int[] dan = list.get(i);
                    if (Cards.compare(dan[0], minCard) > 0) {
                        List<int[]> newList = new ArrayList<>(1);
                        newList.add(dan);
                        return newList;
                    }
                }
            } else {
                for (int[] dan : list) {
                    if (Cards.compare(dan[0], minCard) > 0) {
                        List<int[]> newList = new ArrayList<>(1);
                        newList.add(dan);
                        return newList;
                    }
                }
            }
        }
        //无单拆2
        int _2Idx = Cards.getIndex(2);
        Pile _2Pile = mycards.getPileByIndex(_2Idx);
        if (_2Pile != null && _2Pile.getCount() > 0 && _2Pile.compare(minCard) > 0) {
            int[] cards = _2Pile.getCards();
            for (int card : cards) {
                int pot = Cards.getPoint(card);
                if (pot == 2) {
                    List<int[]> newList = new ArrayList<>(1);
                    newList.add(new int[]{card});
                    return newList;
                }
            }
        }
        //拆对
        list = map.get(WeightType.dui);
        if (list != null && !list.isEmpty()) {
            if (maxDan) {
                for (int i = list.size() - 1; i >= 0; i--) {
                    int[] dui = list.get(i);
                    if (Cards.compare(dui[0], minCard) > 0) {
                        List<int[]> newList = new ArrayList<>(1);
                        newList.add(new int[]{dui[0]});
                        return newList;
                    }
                }
            } else {
                for (int[] dui : list) {
                    if (Cards.compare(dui[0], minCard) > 0) {
                        List<int[]> newList = new ArrayList<>(1);
                        newList.add(new int[]{dui[0]});
                        return newList;
                    }
                }
            }
        }
        //拆五连以上的顶张
        list = map.get(WeightType.danShun);
        if (list != null && !list.isEmpty()) {
            for (int[] shun : list) {
                if (shun.length > 5) {
                    int firstCard = shun[0];
                    int lastCard = shun[shun.length - 1];
                    int maxCard = lastCard;
                    if (firstCard > lastCard) {
                        maxCard = firstCard;
                    }
                    if (Cards.compare(maxCard, minCard) > 0) {
                        List<int[]> newList = new ArrayList<>(1);
                        newList.add(new int[]{maxCard});
                        return newList;
                    }
                }
            }
        }
        //拆三张
        list = map.get(WeightType.san);
        if (list != null && !list.isEmpty()) {
            for (int[] san : list) {
                if (Cards.compare(san[0], minCard) > 0) {
                    List<int[]> newList = new ArrayList<>(1);
                    newList.add(new int[]{san[0]});
                    return newList;
                }
            }
        }
        //拆二连以上的三顺顶张
        list = map.get(WeightType.sanShun);
        if (list != null && !list.isEmpty()) {
            for (int[] shun : list) {
                int shunCount = shun.length / 3;
                if (shunCount >= 3) {
                    int firstCard = shun[0];
                    int lastCard = shun[shun.length - 1];
                    int maxCard = lastCard;
                    if (firstCard > lastCard) {
                        maxCard = firstCard;
                    }
                    if (Cards.compare(maxCard, minCard) > 0) {
                        List<int[]> newList = new ArrayList<>(1);
                        newList.add(new int[]{maxCard});
                        return newList;
                    }
                }
            }
            for (int[] shun : list) {
                int firstCard = shun[0];
                int lastCard = shun[shun.length - 1];
                int maxCard = lastCard;
                if (firstCard > lastCard) {
                    maxCard = firstCard;
                }
                if (Cards.compare(maxCard, minCard) > 0) {
                    List<int[]> newList = new ArrayList<>(1);
                    newList.add(new int[]{maxCard});
                    return newList;
                }
            }
        }
        return map.get(WeightType.zha);
    }

    /**
     * 对牌 </br>
     * 如果手中有对子，则跟之，否则拆4连以上的双顺顶张跟之，否则拆三条跟之，否则拆双顺跟之，否则拆三顺跟之，否则炸之，否则PASS。
     *
     * @param mycards
     * @param minCard
     * @return
     */
    static List<int[]> dui(Cards mycards, int minCard) {
        if (mycards.getCardCount() < 2) return null;
        if (mycards.getCardCount() == 2 && mycards.getPileCount() == 1) {
            Pile pile = mycards.getPileByIndex(mycards.getMinValidIndex());
            if (pile.getPoint() != 0) {
                if (pile.compare(minCard) > 0) {
                    List<int[]> newList = new ArrayList<>(1);
                    newList.add(pile.getCards());
                    return newList;
                }
            }
            return null;
        }
        HandsInfo info = analyseHands(mycards);
        Map<WeightType, List<int[]>> map = info.map;
        List<int[]> list = map.get(WeightType.dui);
        if (list != null && !list.isEmpty()) {
            for (int[] dui : list) {
                if (Cards.compare(dui[0], minCard) > 0) {
                    List<int[]> newList = new ArrayList<>(list.size());
                    newList.add(dui);
                    return newList;
                }
            }
        }
        //拆三张
        list = map.get(WeightType.san);
        if (list != null && !list.isEmpty()) {
            List<int[]> newList = new ArrayList<>(list.size());
            list.forEach((int[] san) -> {
                if (Cards.compare(san[0], minCard) > 0) {
                    newList.add(new int[]{san[0], san[1]});
                }
            });
            if (!newList.isEmpty()) return newList;
        }
        //拆三连以上的连对顶张
        list = map.get(WeightType.duiShun);
        if (list != null && !list.isEmpty()) {
            for (int[] shun : list) {
                int shunCount = shun.length / 2;
                if (shunCount > 3) {
                    int firstCard = shun[0];
                    int lastCard = shun[shun.length - 1];
                    int[] maxDui;
                    if (firstCard > lastCard) {
                        maxDui = new int[]{shun[0], shun[1]};
                    } else {
                        maxDui = new int[]{shun[shun.length - 2], shun[shun.length - 1]};
                    }
                    if (Cards.compare(maxDui[0], minCard) > 0) {
                        List<int[]> newList = new ArrayList<>(list.size());
                        newList.add(maxDui);
                        return newList;
                    }
                }
            }
        }
        //拆三顺
        list = map.get(WeightType.sanShun);
        if (list != null && !list.isEmpty()) {
            for (int[] shun : list) {
                int firstCard = shun[0];
                int lastCard = shun[shun.length - 1];
                int[] maxDui;
                if (firstCard > lastCard) {
                    maxDui = new int[]{shun[0], shun[1]};
                } else {
                    maxDui = new int[]{shun[shun.length - 2], shun[shun.length - 1]};
                }
                if (Cards.compare(maxDui[0], minCard) > 0) {
                    List<int[]> newList = new ArrayList<>(list.size());
                    newList.add(maxDui);
                    return newList;
                }
            }
        }
        return map.get(WeightType.zha);
    }

    static List<int[]> san(Cards mycards, int minCard, HandsInfo[] out) {
        if (mycards.getCardCount() < 3) return null;
        if (mycards.getCardCount() == 3 && mycards.getPileCount() == 1) {
            Pile pile = mycards.getPileByIndex(mycards.getMinValidIndex());
            if (pile.compare(minCard) > 0) {
                List<int[]> newList = new ArrayList<>(1);
                newList.add(pile.getCards());
                return newList;
            }
            return null;
        }
        HandsInfo info = analyseHands(mycards);
        if (out != null && out.length > 0) out[0] = info;
        Map<WeightType, List<int[]>> map = info.map;
        List<int[]> list = map.get(WeightType.san);
        if (list != null && !list.isEmpty()) {
            for (int[] san : list) {
                if (Cards.compare(san[0], minCard) > 0) {
                    List<int[]> newList = new ArrayList<>(list.size());
                    newList.add(san);
                    return newList;
                }
            }
        }
        list = map.get(WeightType.sanShun);
        if (list != null && !list.isEmpty()) {
            for (int[] shun : list) {
                for (int i = 0; i < shun.length; i += 3) {
                    if (Cards.compare(shun[i], minCard) > 0) {
                        List<int[]> newList = new ArrayList<>(list.size());
                        newList.add(new int[]{shun[i], shun[i + 1], shun[i + 2]});
                        return newList;
                    }
                }
            }
        }
        return map.get(WeightType.zha);
    }

    /**
     * 三张 </br>
     * 如果手中有相同牌型的牌则跟之，否则拆三顺跟之，否则炸之，否则PASS
     *
     * @param mycards
     * @param minCard
     * @return
     */
    static List<int[]> san(Cards mycards, int minCard) {
        return san(mycards, minCard, null);
    }

    static List<int[]> zha(Cards mycards, int minCard) {
        return AI.zha(mycards, minCard);
    }

    static List<int[]> danShun(Cards mycards, int minCard, int shunNum) {
        if (shunNum < 5) shunNum = 5;
        int pileCount = mycards.getPileCount();
        int cardTotals = mycards.getCardCount();
        Pile wang = mycards.getJokerPile();
        if (wang != null && wang.getCount() > 0) {
            pileCount--;
            cardTotals -= wang.getCount();
        }
        Pile _2 = mycards.getPileByIndex(Cards.getIndex(2));
        if (_2 != null && _2.getCount() > 0) {
            pileCount--;
            cardTotals -= _2.getCount();
        }
        if (cardTotals < shunNum) return null;
        if (pileCount < shunNum) return null;
        HandsInfo info = analyseHands(mycards);
        Map<WeightType, List<int[]>> map = info.map;
        List<int[]> list = map.get(WeightType.danShun);
        List<int[]> otherDanShun = new ArrayList<>();
        if (list != null && !list.isEmpty()) {
            for (int[] shun : list) {
                if (shun.length == shunNum) {
                    if (Cards.compare(shun[0], minCard) > 0) {
                        List<int[]> newList = new ArrayList<>(1);
                        newList.add(shun);
                        return newList;
                    }
                } else if (shun.length > shunNum) {
                    otherDanShun.add(shun);
                }
            }
        }
        list = map.get(WeightType.duiShun);
        if (list != null && !list.isEmpty()) {
            for (int[] shun : list) {
                int shunCount = shun.length / 2;
                if (shunCount == shunNum) {
                    if (Cards.compare(shun[0], minCard) > 0) {
                        List<int[]> newList = new ArrayList<>(1);
                        int[] danCards = new int[shunNum];
                        for (int i = 0, k = 0; i < shun.length && k < danCards.length; i += 2, k++) {
                            danCards[k] = shun[i];
                        }
                        newList.add(danCards);
                        return newList;
                    }
                } else if (shunCount > shunNum) {
                    int[] danCards = new int[shunNum];
                    for (int i = 0, k = 0; i < shun.length && k < danCards.length; i += 2, k++) {
                        danCards[k] = shun[i];
                    }
                    otherDanShun.add(danCards);
                }
            }
        }
        list = map.get(WeightType.sanShun);
        if (list != null && !list.isEmpty()) {
            for (int[] shun : list) {
                int shunCount = shun.length / 3;
                if (shunCount == shunNum) {
                    if (Cards.compare(shun[0], minCard) > 0) {
                        List<int[]> newList = new ArrayList<>(1);
                        int[] danCards = new int[shunNum];
                        for (int i = 0, k = 0; i < shun.length && k < danCards.length; i += 3, k++) {
                            danCards[k] = shun[i];
                        }
                        newList.add(danCards);
                        return newList;
                    }
                } else if (shunCount > shunNum) {
                    int[] danCards = new int[shunNum];
                    for (int i = 0, k = 0; i < shun.length && k < danCards.length; i += 3, k++) {
                        danCards[k] = shun[i];
                    }
                    otherDanShun.add(danCards);
                }
            }
        }
        if (!otherDanShun.isEmpty()) {
            for (int[] shun : otherDanShun) {
                for (int i = 0; i < shun.length; i++) {
                    int card = shun[i];
                    if (Cards.compare(card, minCard) > 0) {
                        int remainingLen = shun.length - i;
                        if (remainingLen >= shunNum) {
                            int[] danCards = new int[shunNum];
                            System.arraycopy(shun, i, danCards, 0, shunNum);
                            List<int[]> newList = new ArrayList<>(1);
                            newList.add(danCards);
                            return newList;
                        }
                    }
                }
            }
        }
        return map.get(WeightType.zha);
    }

    static List<int[]> duiShun(Cards mycards, int minCard, int shunNum) {
        if (shunNum < 3) shunNum = 3;
        int pileCount = mycards.getPileCount();
        int cardTotals = mycards.getCardCount();
        Pile wang = mycards.getJokerPile();
        if (wang != null && wang.getCount() > 0) {
            pileCount--;
            cardTotals -= wang.getCount();
        }
        Pile _2 = mycards.getPileByIndex(Cards.getIndex(2));
        if (_2 != null && _2.getCount() > 0) {
            pileCount--;
            cardTotals -= _2.getCount();
        }
        if (pileCount < shunNum) return null;
        if (cardTotals / 2 < shunNum) return null;
        HandsInfo info = analyseHands(mycards);
        Map<WeightType, List<int[]>> map = info.map;
        List<int[]> list = map.get(WeightType.duiShun);
        List<int[]> otherDuiShun = new ArrayList<>();
        if (list != null && !list.isEmpty()) {
            for (int[] shun : list) {
                int shunCount = shun.length / 2;
                if (shunCount == shunNum) {
                    if (Cards.compare(shun[0], minCard) > 0) {
                        List<int[]> newList = new ArrayList<>(1);
                        newList.add(shun);
                        return newList;
                    }
                } else {
                    otherDuiShun.add(shun);
                }
            }
        }
        //拆不同张数的对顺
        if (!otherDuiShun.isEmpty()) {
            for (int[] shun : otherDuiShun) {
                for (int i = 0; i < shun.length; i += 2) {
                    if (Cards.compare(shun[i], minCard) > 0) {
                        int remainingLen = shun.length - i;
                        int remainingShunCount = remainingLen / 2;
                        if (remainingShunCount >= shunNum) {
                            int[] duiCards = new int[shunNum * 2];
                            System.arraycopy(shun, i, duiCards, 0, duiCards.length);
                            List<int[]> newList = new ArrayList<>(1);
                            newList.add(duiCards);
                            return newList;
                        }
                    }
                }
            }
            otherDuiShun.clear();
        }
        list = map.get(WeightType.sanShun);
        if (list != null && !list.isEmpty()) {
            for (int[] shun : list) {
                int shunCount = shun.length / 3;
                if (shunCount > shunNum) { //拆不同张数的三顺
                    for (int i = 0; i < shun.length; i += 3) {
                        if (Cards.compare(shun[i], minCard) > 0) {
                            int remainingLen = shun.length - i;
                            int remainingShunCount = remainingLen / 3;
                            if (remainingShunCount >= shunNum) {
                                IntArray duiCards = new IntArray(shunNum * 2);
                                for (int j = i; j < shun.length; j += 3) {
                                    duiCards.add(shun[j]);
                                    duiCards.add(shun[j + 1]);
                                    if (duiCards.size() / 2 == shunNum) break;
                                }
                                List<int[]> newList = new ArrayList<>(1);
                                newList.add(duiCards.toArray());
                                return newList;
                            }
                        }
                    }
                } else if (shunCount == shunNum) { //拆相同张数的三顺
                    if (Cards.compare(shun[0], minCard) > 0) {
                        IntArray duiCards = new IntArray(shunNum * 2);
                        for (int i = 0; i < shun.length; i += 3) {
                            duiCards.add(shun[i]);
                            duiCards.add(shun[i + 1]);
                        }
                        otherDuiShun.add(duiCards.toArray());
                    }
                }
            }
        }
        if (!otherDuiShun.isEmpty()) {
            return otherDuiShun;
        }
        return map.get(WeightType.zha);
    }

    static List<int[]> sanShun(Cards mycards, int minCard, int shunNum, HandsInfo[] out) {
        if (shunNum < 2) shunNum = 2;
        int pileCount = mycards.getPileCount();
        int cardTotals = mycards.getCardCount();
        Pile wang = mycards.getJokerPile();
        if (wang != null && wang.getCount() > 0) {
            pileCount--;
            cardTotals -= wang.getCount();
        }
        Pile _2 = mycards.getPileByIndex(Cards.getIndex(2));
        if (_2 != null && _2.getCount() > 0) {
            pileCount--;
            cardTotals -= _2.getCount();
        }
        if (pileCount < shunNum) return null;
        if (cardTotals / 3 < shunNum) return null;
        HandsInfo info = analyseHands(mycards);
        if (out != null && out.length > 0) out[0] = info;
        Map<WeightType, List<int[]>> map = info.map;
        List<int[]> list = map.get(WeightType.sanShun);
        List<int[]> otherSanShun = new ArrayList<>();
        if (list != null && !list.isEmpty()) {
            for (int[] shun : list) {
                int shunCount = shun.length / 3;
                if (shunCount == shunNum) {
                    if (Cards.compare(shun[0], minCard) > 0) {
                        List<int[]> newList = new ArrayList<>(1);
                        newList.add(shun);
                        return newList;
                    }
                } else if (shunCount > shunNum) {
                    otherSanShun.add(shun);
                }
            }
        }
        if (!otherSanShun.isEmpty()) {
            for (int[] shun : list) {
                for (int i = 0; i < shun.length; i += 3) {
                    if (Cards.compare(shun[i], minCard) > 0) {
                        int remainingLen = shun.length - i;
                        int remainingShunCount = remainingLen / 3;
                        if (remainingShunCount >= shunNum) {
                            int[] sanCards = new int[shunNum * 3];
                            System.arraycopy(shun, i, sanCards, 0, sanCards.length);
                            List<int[]> newList = new ArrayList<>(1);
                            newList.add(sanCards);
                            return newList;
                        }
                    }
                }
            }
        }
        return map.get(WeightType.zha);
    }

    static List<int[]> sanShun(Cards mycards, int minCard, int shunNum) {
        return sanShun(mycards, minCard, shunNum, null);
    }

    static List<Integer> danOfDai(Cards mycards, int amount) {
        List<Integer> result = new ArrayList<>(mycards.getCardCount());
        HandsInfo info = analyseHands(mycards);
        Map<WeightType, List<int[]>> map = info.map;
        List<int[]> list = map.get(WeightType.dan);
        //有单出单
        if (list != null && !list.isEmpty()) {
            for (int[] dan : list) {
                result.add(dan[0]);
            }
            if (result.size() >= amount) return result;
        }
        //拆对
        list = map.get(WeightType.dui);
        if (list != null && !list.isEmpty()) {
            for (int[] dui : list) {
                for (int card : dui) {
                    result.add(card);
                }
            }
            if (result.size() >= amount) return result;
        }
        //拆五连以上的最小
        list = map.get(WeightType.danShun);
        if (list != null && !list.isEmpty()) {
            for (int[] shun : list) {
                if (shun.length > 5) {
                    result.add(shun[0]);
                }
            }
            if (result.size() >= amount) return result;
        }
        //拆三张
        list = map.get(WeightType.san);
        if (list != null && !list.isEmpty()) {
            for (int[] san : list) {
                for (int card : san) {
                    result.add(card);
                }
            }
            if (result.size() >= amount) return result;
        }
        //拆二连以上的三顺最小
        list = map.get(WeightType.sanShun);
        if (list != null && !list.isEmpty()) {
            for (int[] shun : list) {
                int shunCount = shun.length / 3;
                if (shunCount > 2) {
                    for (int i = 0; i < 3; i++) {
                        result.add(shun[i]);
                    }
                }
            }
            if (result.size() >= amount) return result;
        }
        //拆三连以上的对顺最小
        list = map.get(WeightType.duiShun);
        if (list != null && !list.isEmpty()) {
            for (int[] shun : list) {
                int shunCount = shun.length / 2;
                if (shunCount > 3) {
                    for (int i = 0; i < 2; i++) {
                        result.add(shun[i]);
                    }
                }
            }
        }
        return result;
    }

    static List<int[]> duiOfDai(Cards mycards, int amount) {
        List<int[]> result = new ArrayList<>(mycards.getPileCount());
        HandsInfo info = analyseHands(mycards);
        Map<WeightType, List<int[]>> map = info.map;
        List<int[]> list = map.get(WeightType.dui);
        if (list != null && !list.isEmpty()) {
            result.addAll(list);
            if (result.size() >= amount) return result;
        }
        list = map.get(WeightType.duiShun);
        List<int[]> otherShunList = new ArrayList<>();
        if (list != null && !list.isEmpty()) {
            for (int[] shun : list) {
                int shunCount = shun.length / 2;
                if (shunCount > 3) {
                    result.add(new int[]{shun[0], shun[1]});
                } else if (shunCount == 3) {
                    otherShunList.add(shun);
                }
            }
            if (result.size() >= amount) return result;
        }
        list = map.get(WeightType.san);
        if (list != null && !list.isEmpty()) {
            for (int[] san : list) {
                result.add(new int[]{san[0], san[1]});
            }
            if (result.size() >= amount) return result;
        }
        if (!otherShunList.isEmpty()) {
            for (int[] shun : otherShunList) {
                for (int i = 0; i < shun.length; i += 2) {
                    result.add(new int[]{shun[i], shun[i + 1]});
                }
            }
            otherShunList.clear();
            if (result.size() >= amount) return result;
        }
        list = map.get(WeightType.sanShun);
        if (list != null && !list.isEmpty()) {
            for (int[] shun : list) {
                for (int i = 0; i < shun.length; i += 3) {
                    result.add(new int[]{shun[i], shun[i + 1]});
                    if (result.size() >= amount) return result;
                }
            }
        }
        return result;
    }

    static List<int[]> sandai1(Cards mycards, int minCard) {
        if (mycards.getCardCount() < 4) return null;
        if (mycards.getPileCount() < 2) return null;
        HandsInfo[] infos = new HandsInfo[1];
        List<int[]> list = san(mycards, minCard, infos);
        if (list == null || list.isEmpty()) return null;
        Pile pile_wang = mycards.getJokerPile();
        Pile pile_2 = mycards.getPileByIndex(Cards.getIndex(2));
        HandsInfo info = infos[0];
        for (int[] san : list) {
            if (san.length == 3) {
                mycards.removeCard(san);
                List<Integer> dan;
                if ((pile_wang == null || pile_wang.getCount() == 0 || pile_wang.getCount() == 2)
                        && (pile_2 == null || pile_2.getCount() == 0)) {
                    dan = danOfDai(mycards, 1);
                } else {
                    dan = danOfDai(mycards, mycards.getCardCount());
                }
                for (Integer danCard : dan) {
                    int point = Cards.getPoint(danCard);
                    if ((point != 0 && point != 2) || info.hands <= 2) {
                        int[] san1 = new int[4];
                        System.arraycopy(san, 0, san1, 0, san.length);
                        san1[san1.length - 1] = danCard;
                        List<int[]> newList = new ArrayList<>(1);
                        newList.add(san1);
                        return newList;
                    }
                }
                mycards.addCard(san);
            }
        }
        return info.map.get(WeightType.zha);
    }

    static List<int[]> sandai2(Cards mycards, int minCard) {
        if (mycards.getCardCount() < 5) return null;
        if (mycards.getPileCount() < 2) return null;
        HandsInfo[] infos = new HandsInfo[1];
        List<int[]> list = san(mycards, minCard, infos);
        if (list == null || list.isEmpty()) return null;
        Pile pile_2 = mycards.getPileByIndex(Cards.getIndex(2));
        HandsInfo info = infos[0];
        for (int[] san : list) {
            if (san.length == 3) {
                mycards.removeCard(san);
                List<int[]> duis;
                if (pile_2 == null || pile_2.getCount() < 2) {
                    duis = duiOfDai(mycards, 1);
                } else {
                    duis = duiOfDai(mycards, mycards.getCardCount() / 2);
                }
                for (int[] dui : duis) {
                    int point = Cards.getPoint(dui[0]);
                    if (point != 2 || info.hands <= 2) {
                        int[] san2 = new int[5];
                        System.arraycopy(san, 0, san2, 0, san.length);
                        System.arraycopy(dui, 0, san2, san.length, dui.length);
                        List<int[]> newList = new ArrayList<>(1);
                        newList.add(san2);
                        return newList;
                    }
                }
                mycards.addCard(san);
            }
        }
        return info.map.get(WeightType.zha);
    }

    static List<int[]> feiji1(Cards mycards, int minCard, int shunNum) {
        int minCountLimit = shunNum * 3 + shunNum;
        if (mycards.getCardCount() < minCountLimit) return null;
        HandsInfo[] infos = new HandsInfo[1];
        List<int[]> list = sanShun(mycards, minCard, shunNum, infos);
        if (list == null || list.isEmpty()) return null;
        Pile pile_wang = mycards.getJokerPile();
        Pile pile_2 = mycards.getPileByIndex(Cards.getIndex(2));
        HandsInfo info = infos[0];
        for (int[] shun : list) {
            if (shun.length != 4 && shun.length != 2) {
                mycards.removeCard(shun);
                List<Integer> dans;
                if ((pile_wang == null || pile_wang.getCount() == 0 || pile_wang.getCount() == 2)
                        && (pile_2 == null || pile_2.getCount() == 0)) {
                    dans = danOfDai(mycards, shunNum);
                } else {
                    dans = danOfDai(mycards, mycards.getCardCount());
                    if (info.hands > 2) {
                        Set<Integer> set = new HashSet<>(4);
                        for (Integer dan : dans) {
                            int point = Cards.getPoint(dan);
                            if (point == 0 || point == 2) {
                                set.add(dan);
                            }
                        }
                        set.forEach(dans::remove);
                    }
                }
                int size = dans.size();
                if (size >= shunNum) {
                    IntArray group = new IntArray(shunNum * 3 + shunNum);
                    group.addAll(shun);
                    for (int i = 0; i < shunNum; i++) {
                        group.add(dans.get(i));
                    }
                    List<int[]> newList = new ArrayList<>(1);
                    newList.add(group.toArray());
                    return newList;
                }
                mycards.addCard(shun);
            }
        }
        return info.map.get(WeightType.zha);
    }

    static List<int[]> feiji2(Cards mycards, int minCard, int shunNum) {
        int minCountLimit = shunNum * 3 + shunNum * 2;
        if (mycards.getCardCount() < minCountLimit) return null;
        HandsInfo[] infos = new HandsInfo[1];
        List<int[]> list = sanShun(mycards, minCard, shunNum, infos);
        if (list == null || list.isEmpty()) return null;
        Pile pile_2 = mycards.getPileByIndex(Cards.getIndex(2));
        HandsInfo info = infos[0];
        for (int[] shun : list) {
            if (shun.length != 4 && shun.length != 2) {
                mycards.removeCard(shun);
                List<int[]> duis;
                if (pile_2 == null || pile_2.getCount() == 0) {
                    duis = duiOfDai(mycards, shunNum);
                } else {
                    duis = duiOfDai(mycards, mycards.getCardCount());
                    if (info.hands > 2) {
                        Set<int[]> set = new HashSet<>();
                        for (int[] dui : duis) {
                            int point = Cards.getPoint(dui[0]);
                            if (point == 2) {
                                set.add(dui);
                            }
                        }
                        set.forEach(duis::remove);
                    }
                }
                int size = duis.size();
                if (size >= shunNum) {
                    IntArray group = new IntArray(shunNum * 3 + shunNum * 2);
                    group.addAll(shun);
                    for (int i = 0; i < shunNum; i++) {
                        int[] dui = duis.get(i);
                        group.addAll(dui);
                    }
                    List<int[]> newList = new ArrayList<>(1);
                    newList.add(group.toArray());
                    return newList;
                }
                mycards.addCard(shun);
            }
        }
        return info.map.get(WeightType.zha);
    }

    /**
     * 四带两单张，有炸弹则炸，没有直接pass
     *
     * @param mycards 手牌
     * @return
     */
    static List<int[]> sidai1(Cards mycards) {
        return zha(mycards, -1);
    }

    /**
     * 四带两对，有炸弹则炸，没有直接pass
     *
     * @param mycards 手牌
     * @return
     */
    static List<int[]> sidai2(Cards mycards) {
        return zha(mycards, -1);
    }

    ///////////////////////////////////////////////////////////////////////////////////

    static int count(Map<WeightType, List<int[]>> map, WeightType type) {
        int count = 0;
        if (type == WeightType.dan) {
            List<int[]> list = map.get(WeightType.dan);
            if (list != null && !list.isEmpty()) {
                count += list.size();
            }
        } else if (type == WeightType.dui) {
            List<int[]> list = map.get(WeightType.dui);
            if (list != null && !list.isEmpty()) {
                count += list.size();
            }
        } else if (type == WeightType.san) {
            List<int[]> list = map.get(WeightType.san);
            if (list != null && !list.isEmpty()) {
                count += list.size();
            }
            list = map.get(WeightType.sanShun);
            if (list != null && !list.isEmpty()) {
                for (int[] shun : list) {
                    int shunCount = shun.length / 3;
                    count += shunCount;
                }
            }
        }
        return count;
    }

    /**
     * 超出两手牌移除单牌或对牌中的王或2
     *
     * @param list
     * @return
     */
    static List<int[]> removeWangAnd2(List<int[]> list) {
        int i = 0, max = list.size();
        while (i < max) {
            int[] dan = list.get(i);
            int point = Cards.getPoint(dan[0]);
            if (point == 2) {
                list.remove(i);
                max--;
                continue;
            }
            i++;
        }
        return list;
    }

    /**
     * 出单或对时考虑搭配三条和三顺
     *
     * @param info
     * @return
     */
    static List<int[]> danOrDui(HandsInfo info) {
        Map<WeightType, List<int[]>> map = info.map;
        List<int[]> list = map.get(WeightType.dan);
        if (list != null && !list.isEmpty()) {
            if (info.hands > 2) {
                //超出两手牌2和王不能作为带牌
                removeWangAnd2(list);
            }
            if (!list.isEmpty()) {
                int danCount = list.size();
                List<int[]> sans = map.get(WeightType.san);
                if (sans != null && !sans.isEmpty()) {
                    for (int[] san : sans) {
                        if (Cards.getPoint(san[0]) == 2 && info.hands > 2) continue;
                        int[] group = new int[4];
                        System.arraycopy(san, 0, group, 0, san.length);
                        group[san.length] = list.get(0)[0];
                        List<int[]> newList = new ArrayList<>(1);
                        newList.add(group);
                        return newList;
                    }
                }
                List<int[]> sanShuns = map.get(WeightType.sanShun);
                if (sanShuns != null && !sanShuns.isEmpty()) {
                    int[] shun = sanShuns.get(0);
                    int shunCount = shun.length / 3;
                    if (danCount >= shunCount) {
                        IntArray group = new IntArray(shun.length + shunCount);
                        group.addAll(shun);
                        for (int i = 0; i < shunCount; i++) {
                            group.add(list.get(i)[0]);
                        }
                        List<int[]> newList = new ArrayList<>(1);
                        newList.add(group.toArray());
                        return newList;
                    }
                    //单牌数量少于三顺中的三张数量
                    int len = danCount * 3;
                    IntArray group = new IntArray(len + danCount);
                    for (int i = 0; i < len; i += 3) {
                        group.add(shun[i]);
                        group.add(shun[i + 1]);
                        group.add(shun[i + 2]);
                    }
                    for (int i = 0; i < danCount; i++) {
                        group.add(list.get(i)[0]);
                    }
                    List<int[]> newList = new ArrayList<>(1);
                    newList.add(group.toArray());
                    return newList;
                }
            }
            //return list.subList(0, 1);
        }
        list = map.get(WeightType.dui);
        if (list != null && !list.isEmpty()) {
            if (info.hands > 2) {
                //超出两手牌2和王不能作为带牌
                removeWangAnd2(list);
            }
            if (!list.isEmpty()) {
                int duiCount = list.size();
                List<int[]> sans = map.get(WeightType.san);
                if (sans != null && !sans.isEmpty()) {
                    for (int[] san : sans) {
                        if (Cards.getPoint(san[0]) == 2 && info.hands > 2) continue;
                        int[] group = new int[5];
                        System.arraycopy(san, 0, group, 0, san.length);
                        int[] dui = list.get(0);
                        System.arraycopy(dui, 0, group, san.length, dui.length);
                        List<int[]> newList = new ArrayList<>(1);
                        newList.add(group);
                        return newList;
                    }
                }
                List<int[]> sanShuns = map.get(WeightType.sanShun);
                if (sanShuns != null && !sanShuns.isEmpty()) {
                    int[] shun = sanShuns.get(0);
                    int shunCount = shun.length / 3;
                    if (duiCount >= shunCount) {
                        IntArray group = new IntArray(shun.length + shunCount * 2);
                        group.addAll(shun);
                        for (int i = 0; i < shunCount; i++) {
                            group.addAll(list.get(i));
                        }
                        List<int[]> newList = new ArrayList<>(1);
                        newList.add(group.toArray());
                        return newList;
                    }
                    //对牌数量少于三顺中的三张数量
                    int len = duiCount * 3;
                    IntArray group = new IntArray(len + duiCount * 2);
                    for (int i = 0; i < len; i += 3) {
                        group.add(shun[i]);
                        group.add(shun[i + 1]);
                        group.add(shun[i + 2]);
                    }
                    for (int i = 0; i < duiCount; i++) {
                        group.addAll(list.get(i));
                    }
                    List<int[]> newList = new ArrayList<>(1);
                    newList.add(group.toArray());
                    return newList;
                }
            }
            //return list.subList(0, 1);
        }
        return null;
    }

    /**
     * 出三条或三顺时考虑搭配单牌或对牌
     *
     * @param info
     * @return
     */
    static List<int[]> sanOrSanShun(HandsInfo info) {
        Map<WeightType, List<int[]>> map = info.map;
        List<int[]> sans = map.get(WeightType.san);
        List<int[]> sanShuns = map.get(WeightType.sanShun);
        if (sans == null && sanShuns == null) return null;
        List<int[]> list;
        if (sans != null && sanShuns != null) {
            list = sans;
            list.addAll(sanShuns);
        } else if (sans != null) {
            list = sans;
        } else {
            list = sanShuns;
        }
        if (list.isEmpty()) return null;
        List<int[]> dans = map.get(WeightType.dan);
        if (dans != null && !dans.isEmpty()) {
            if (info.hands > 2) {
                //超出两手牌2和王不能作为带牌
                removeWangAnd2(dans);
            }
            for (int[] sanShun : list) {
                if (Cards.getPoint(sanShun[0]) == 2 && info.hands > 2) continue;
                int shunCount = sanShun.length / 3;
                if (dans.size() >= shunCount) {
                    int[] group = new int[shunCount * 3 + shunCount];
                    System.arraycopy(sanShun, 0, group, 0, sanShun.length);
                    for (int i = 0, k = sanShun.length; i < shunCount; i++, k++) {
                        group[k] = dans.get(i)[0];
                    }
                    List<int[]> result = new ArrayList<>(1);
                    result.add(group);
                    return result;
                }
            }
        }
        List<int[]> danShuns = map.get(WeightType.danShun);
        if (danShuns != null && !danShuns.isEmpty()) {
            int[] sanShun = null;
            for (int[] shun : list) {
                if (Cards.getPoint(shun[0]) == 2 && info.hands > 2) continue;
                sanShun = shun;
                break;
            }
            if (sanShun != null) {
                int shunCount = sanShun.length / 3;
                IntArray danCards = new IntArray(shunCount);
                for (int[] shun : danShuns) {
                    if (shun.length > 5) {
                        int offset = shun.length - 5;
                        for (int i = 0; i < offset; i++) {
                            danCards.add(shun[i]);
                        }
                        if (danCards.size() >= shunCount) break;
                    }
                }
                if (danCards.size() >= shunCount) {
                    int[] group = new int[shunCount * 3 + shunCount];
                    System.arraycopy(sanShun, 0, group, 0, sanShun.length);
                    for (int i = 0, k = sanShun.length; i < shunCount; i++, k++) {
                        group[k] = danCards.get(i);
                    }
                    List<int[]> result = new ArrayList<>(1);
                    result.add(group);
                    return result;
                }
            }
        }
        List<int[]> duis = map.get(WeightType.dui);
        if (duis != null && !duis.isEmpty()) {
            if (info.hands > 2) {
                //超出两手牌2和王不能作为带牌
                removeWangAnd2(duis);
            }
            for (int[] sanShun : list) {
                if (Cards.getPoint(sanShun[0]) == 2 && info.hands > 2) continue;
                int shunCount = sanShun.length / 3;
                if (duis.size() >= shunCount) {
                    IntArray group = new IntArray(shunCount * 3 + shunCount * 2);
                    group.addAll(sanShun);
                    for (int i = 0; i < shunCount; i++) {
                        group.addAll(duis.get(i));
                    }
                    List<int[]> result = new ArrayList<>(1);
                    result.add(group.toArray());
                    return result;
                }
            }
        }
        List<int[]> duiShuns = map.get(WeightType.duiShun);
        if (duiShuns != null && !duiShuns.isEmpty()) {
            int[] sanShun = null;
            for (int[] shun : list) {
                if (Cards.getPoint(shun[0]) == 2 && info.hands > 2) continue;
                sanShun = shun;
                break;
            }
            if (sanShun != null) {
                int shunCount = sanShun.length / 3;
                List<int[]> duiArr = new ArrayList<>(shunCount);
                for (int[] duiShun : duiShuns) {
                    int scount = duiShun.length / 2;
                    if (scount > 3) {
                        int offset = scount - 3;
                        for (int i = 0, k = 0; i < offset; i++, k += 2) {
                            duiArr.add(new int[]{duiShun[k], duiShun[k + 1]});
                        }
                    }
                    if (duiArr.size() >= shunCount) break;
                }
                if (duiArr.size() >= shunCount) {
                    IntArray group = new IntArray(shunCount * 3 + shunCount * 2);
                    group.addAll(sanShun);
                    for (int i = 0; i < shunCount; i++) {
                        group.addAll(duiArr.get(i));
                    }
                    List<int[]> result = new ArrayList<>(1);
                    result.add(group.toArray());
                    return result;
                }
            }
        }
        return null;
    }

    public static List<int[]> zhuDong(Cards mycards, boolean maxDan) {
        HandsInfo info = analyseHands(mycards);
        List<int[]> list = danOrDui(info);
        if (list != null && !list.isEmpty()) return list;
        list = sanOrSanShun(info);
        if (list != null && !list.isEmpty()) return list;
        Map<WeightType, List<int[]>> map = info.map;
        list = map.get(WeightType.duiShun);
        if (list != null && !list.isEmpty()) {
            return list.subList(0, 1);
        }
        list = map.get(WeightType.danShun);
        if (list != null && !list.isEmpty()) {
            return list.subList(0, 1);
        }
        list = map.get(WeightType.sanShun);
        if (list != null && !list.isEmpty()) {
            return list.subList(0, 1);
        }
        list = map.get(WeightType.dui);
        if (list != null && !list.isEmpty()) {
            return list.subList(0, 1);
        }
        list = map.get(WeightType.dan);
        if (list != null && !list.isEmpty()) {
            if (!maxDan) return list.subList(0, 1);
            return list.subList(list.size() - 1, list.size());
        }
        list = map.get(WeightType.san);
        if (list != null && !list.isEmpty()) {
            return list.subList(0, 1);
        }
        return map.get(WeightType.zha);
    }

    public static List<int[]> beiDong(Cards mycards, Cards prevChuCards, CardType prevCardType, boolean maxDan, boolean lianZha) {
        Pile min = AI.getMinPile(prevChuCards, prevCardType);
        int minCard = min.getOneCard();
        List<int[]> groups = null;
        switch (prevCardType) {
            case dan:
                groups = AIBot.dan(mycards, minCard, maxDan);
                break;
            case dui:
                groups = AIBot.dui(mycards, minCard);
                break;
            case san:
                groups = AIBot.san(mycards, minCard);
                break;
            case san_dan:
                groups = AIBot.sandai1(mycards, minCard);
                break;
            case san_dui:
                groups = AIBot.sandai2(mycards, minCard);
                break;
            case dan_shun:
                groups = AIBot.danShun(mycards, minCard, prevChuCards.getPileCount());
                break;
            case dui_shun:
                groups = AIBot.duiShun(mycards, minCard, prevChuCards.getPileCount());
                break;
            case san_shun:
                groups = AIBot.sanShun(mycards, minCard, prevChuCards.getPileCount());
                break;
            case feiji_dan:
                groups = AIBot.feiji1(mycards, minCard, prevChuCards.getCardCount() / 4);
                break;
            case feiji_dui:
                groups = AIBot.feiji2(mycards, minCard, prevChuCards.getCardCount() / 5);
                break;
            case si_dan:
            case si_dui:
                groups = AIBot.zha(mycards, -1);
                break;
            case zha:
                groups = AIBot.zha(mycards, minCard);
                if (lianZha) {
                    List<int[]> lzhas = AI.lianZha(mycards, -1, 1);
                    if (!lzhas.isEmpty()) groups.addAll(lzhas);
                }
                break;
            case lianzha:
                if (lianZha) {
                    groups = AI.lianZha(mycards, minCard, prevChuCards.getPileCount());
                }
                break;
        }
        return groups;
    }

    /**
     * 计算显示加倍操作
     *
     * @param mycards 手牌
     * @return 权值，根据此值显示加倍按钮；小于5分不加倍，大于等于5分时叫一倍；大于或等于7分叫超级加倍
     */
    public static int calcJiabeiScore(Cards mycards) {
        int score = 0;
        Pile wangPile = mycards.getJokerPile();
        if (wangPile != null) {
            if (wangPile.getCount() == 2) {
                score += 8;
            } else if (wangPile.getCount() == 1) {
                if (wangPile.isDaJoker()) {
                    score += 4;
                } else if (wangPile.isXiJoker()) {
                    score += 3;
                }
            }
        }
        Pile twoPile = mycards.getPileByIndex(Cards.getIndex(2));
        if (twoPile != null && twoPile.getCount() > 0 && twoPile.getCount() != 4) {
            score += twoPile.getCount() * 2;
        }
        for (int i = mycards.getMinValidIndex(); i <= mycards.getMaxValidIndex(); i++) {
            Pile pile = mycards.getPileByIndex(i);
            if (pile == null) continue;
            if (pile.getCount() == 4) {
                score += 6;
            }
        }
        return score;
    }

    public static void main(String[] args) {
        //500, 414, 314, 113, 412, 212, 311, 410, 110, 109, 108, 407, 307, 107, 306, 206, 106, 305, 205, 105, 404, 304, 204
//        int[] cards = new int[]{
//                600, 500, 102, 214, 114, 213, 210, 409, 408, 208, 407, 207, 306, 305, 105, 404, 303
//        };
//        HandCards mycards = new HandCards();
//        mycards.addCard(cards);
//        List<int[]> result = zhuDong(mycards, false);
//        for (int[] group : result) {
//            System.out.println(toString(group));
//        }

//        HandsInfo info = analyseHands(mycards);
//        System.out.println(JsonUtils.toJsonString(info));
//        System.out.println(Arrays.toString(mycards.getCards()));

//        List<int[]> danShun = sanShun(mycards, 303, 2);
//        System.out.println(Arrays.deepToString(danShun.toArray()));
        //List<Integer> dan = danOfDai(mycards, mycards.getCardCount());
        //System.out.println(Arrays.toString(dan.toArray()));

//        while (mycards.getCardCount() > 0) {
//            List<int[]> result = zhuDong(mycards, false);
//            if (result == null) continue;
//            for (int[] group : result) {
//                mycards.removeCard(group);
//                System.out.println(toString(group));
//            }
//        }

        int[] cards = new int[]{
                600, 500, 102, 214, 114, 213, 210, 409, 408, 208, 407, 207, 306, 305, 105, 404, 303
        };
        Cards mycards = new Cards();
        mycards.addCard(cards);

        cards = new int[]{411, 211, 111, 412, 312, 212, 107, 310};
        Cards prevChuCards = new Cards();
        prevChuCards.addCard(cards);
        List<int[]> result = beiDong(mycards, prevChuCards, CardType.feiji_dan, false, true);
        for (int[] group : result) {
            System.out.println(toString(group));
        }
    }
}
